package vg.modules.attribute_panel;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;

import org.apache.commons.lang.mutable.MutableBoolean;

import vg.modules.attribute_panel.components.AttributeTableCellEditor;
import vg.modules.attribute_panel.components.AttributeTableCellRenderer;
import vg.modules.attribute_panel.components.AttributeTableModel;
import vg.modules.attribute_panel.components.data.AttributeRow;
import vg.modules.attribute_panel.components.data.AttributeShower;
import vg.modules.attribute_panel.components.data.AttributeValue;
import vg.services.data_base_manager.data.graph.Attribute;
import vg.services.graph_view_manager.interfaces.graph_view.AGraphView;
import vg.services.graph_view_manager.interfaces.tab.ATab;
import vg.services.main_manager.MainManager;
import vg.services.plugin_manager.event_and_request.event.AUIEvent;
import vg.services.plugin_manager.event_and_request.event.UIEventChangeTab;
import vg.services.user_interface_manager.additional_swing_components.TableCellTooltipRender;
import vg.services.user_interface_manager.interfaces.AUserInterfaceElement;

/**
 * This class is a filter attribute panel for vertexes and edges.
 * @author tzolotuhin
 */
public class AttributePanel extends AUserInterfaceElement {
	// Defines
	private final static int DEF_ROW_HEIGHT = 22;
	
	// Main data
	private AttributeTableModel tableModel = null;
	private JTable table;
	private AGraphView graphView;
	private Observer listener;
	private Map<Attribute,MutableBoolean> vertexAttributes;
	private Map<Attribute,MutableBoolean> edgeAttributes;
	
	// Swing components
	private JScrollPane scroll;
	private JButton applyButton;
	private JPanel view;
	private JCheckBox selectAll;
	private JLabel selectAllLabel;
	
	// Mutex
	private final Object generalMutex = new Object();
	
	/**
	 * Constructor.
	 */
	public AttributePanel() {
		super("Attribute panel", null);
		init();
		
		this.listener = new Observer() {
			public void update(Observable o, Object arg) {
				synchronized (generalMutex) {
					if(graphView != null) {
						vertexAttributes = graphView.getGraphInformation().getSelectionVertexAttributes();
						edgeAttributes = graphView.getGraphInformation().getSelectionEdgeAttributes();
						if(SwingUtilities.isEventDispatchThread()) {
							buildPanel();														
						} else {
							SwingUtilities.invokeLater(new Runnable() {
								public void run() {
									synchronized (generalMutex) {
										buildPanel();
									}			
								}
							});								
						}
					} else {
						MainManager.log.printError("[" + this.getClass().getName() + ".AttributePanel.run] [BAD] IGraphView = null");
					}
				}
			}
		};			
	}
	
	public void update(Observable o, Object arg) {
		if(arg instanceof AUIEvent) {
			AUIEvent event = (AUIEvent)arg;
			switch(event.getType()) {
				case DEF_CHANGE_TAB: {
					UIEventChangeTab bufEvent = (UIEventChangeTab)event;
					ATab tab = bufEvent.getTab();
					if (tab != null) {
						switch (tab.getTabType()) {
							case ATab.DEF_GRAPH_VIEW_TAB_TYPE: {
								setGraphView((AGraphView)tab);								
								break;
							}							
						}
					}
					break;
				}
				
				case DEF_CHANGE_UI_STYLE: {
					updateUITheme();
					break;
				}
				
				default: break;
			}
		}
	}
	
	public JComponent getView() {
		synchronized (generalMutex) {
			return view;
		}
	}
	
	/**
	 * This method updates UI theme.
	 */
	public void updateUITheme() {
		if(SwingUtilities.isEventDispatchThread()) {
			synchronized (generalMutex) {
				buildPanel();
				SwingUtilities.updateComponentTreeUI(view);
			}
		} else {
			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					synchronized (generalMutex) {
						buildPanel();
						SwingUtilities.updateComponentTreeUI(view);
					}
				}
			});
		}
	}
//=============================================================================
//-----------------PRIVATE METHODS---------------------------------------------
	/**
	 * This method sets new graph view to attributes panel.
	 */
	private void setGraphView(AGraphView graphView) {
		synchronized (generalMutex) {
			// Remove all listeners from old graph view
			if(this.graphView != null) this.graphView.getGraphObservable().deleteAllObservers();
			// Set new graph view
			this.graphView = graphView;
			if(this.graphView != null) {
				this.vertexAttributes = this.graphView.getGraphInformation().getSelectionVertexAttributes();
				this.edgeAttributes = this.graphView.getGraphInformation().getSelectionEdgeAttributes();
				this.graphView.getGraphObservable().addObserver(this.listener);
			} else {
				this.vertexAttributes = new HashMap<Attribute, MutableBoolean>();
				this.edgeAttributes = new HashMap<Attribute, MutableBoolean>();
			}
			// Rebuild panel
			if(SwingUtilities.isEventDispatchThread()) {
				buildPanel();
			} else {
				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						synchronized (generalMutex) {
							buildPanel();
						}
					}
				});				
			}
		}
	}
	
	private void apply() {
		HashSet<String>list_v = new HashSet<String>();
		HashSet<String>list_e = new HashSet<String>();
		for (int i = 0 ; i < this.tableModel.getRowCount() ; i++) {
			AttributeShower shower = (AttributeShower)this.tableModel.getValueAt(i, 0);
			if(shower.isShow() && this.tableModel.getValueAt(i, 3).equals("V")) {
				list_v.add((String)this.tableModel.getValueAt(i, 1));
			}
			if(shower.isShow() && this.tableModel.getValueAt(i, 3).equals("E")) {
				list_e.add((String)this.tableModel.getValueAt(i, 1));
			}
		}
		
		if(this.graphView != null) {
			this.graphView.getHighLevelGraphEditor().showAttributes(list_v, list_e);
			this.vertexAttributes = AttributePanel.this.graphView.getGraphInformation().getSelectionVertexAttributes();
			this.edgeAttributes = AttributePanel.this.graphView.getGraphInformation().getSelectionEdgeAttributes();
			if (SwingUtilities.isEventDispatchThread()) {
				buildPanel();
			} else {
				SwingUtilities.invokeLater(new Runnable() {
					@Override
					public void run() {
						synchronized (generalMutex) {
							buildPanel();
						}
					}
				});
			}
		} else {
			MainManager.log.printError("[" + this.getClass().getName() + ".AttributePanel.run] [BAD] IGraphView = null");
		}
	}
	
	/**
	 * This method rebuilds attributes panel.
	 * Note: Call this method from EDT.
	 */
	private void buildPanel() {
		this.view.removeAll();
		//---------------------------------------
		HashMap<String, AttributeRow>data = new HashMap<String, AttributeRow>();
		boolean checkAll = true;
		// Finding of attributes and their values
		if(this.vertexAttributes != null) {
			for(Attribute bufAttr : this.vertexAttributes.keySet()) {
				if(bufAttr.getName() != null && bufAttr.getValue() != null) {
					AttributeRow value = data.get(bufAttr.getName());
					MutableBoolean visible = this.vertexAttributes.get(bufAttr);
					if(value == null) {
						value = new AttributeRow(bufAttr.getName(), visible.booleanValue(), "V", new AttributeValue());
						data.put(bufAttr.getName(), value);
					}
					switch(value.getShow()) {
						case AttributeShower.DEF_UNSHOW_GREEN:
						{
							checkAll = false;
							if(visible.booleanValue()) {
								value.getShower().setYellowShow();
							}
							break;
						}
						case AttributeShower.DEF_SHOW_GREEN:
						{
							if(!visible.booleanValue()) {
								value.getShower().setYellowShow();
							}
							break;
						}
					}
					value.addValue(bufAttr.getValue());
				} else {
					MainManager.log.printError("[" + this.getClass().getName() + ".buildPanel]" + " Error in attribute panel. Attribute name or attribute value = null(Vertex)");
				}
			}
		}
		//-----------------------------------
		if(this.edgeAttributes != null) {
			for(Attribute bufAttr : this.edgeAttributes.keySet()) {
				if(bufAttr.getName() != null && bufAttr.getValue() != null) {
					AttributeRow value = data.get(bufAttr.getName());
					MutableBoolean visible = this.edgeAttributes.get(bufAttr);
					if(value == null) {
						value = new AttributeRow(bufAttr.getName(), visible.booleanValue(), "E", new AttributeValue());
						data.put(bufAttr.getName(), value);
					}
					switch(value.getShow()) {
						case AttributeShower.DEF_UNSHOW_GREEN:
						{
							checkAll = false;
							if(visible.booleanValue()) {
								value.getShower().setYellowShow();
							}
							break;
						}
						case AttributeShower.DEF_SHOW_GREEN:
						{
							if(!visible.booleanValue()) {
								value.getShower().setYellowShow();
							}
							break;
						}
					}
					value.addValue(bufAttr.getValue());
				} else {
					MainManager.log.printError("[" + this.getClass().getName() + ".buildPanel]" + " Error in attribute panel. Attribute name or attribute value = null(Edge)");
				}
			}
		}
		//Creating components of the filter window
		this.tableModel = new AttributeTableModel(data.values(), this.selectAll);
		//---------------------------------------
		GridBagConstraints gbc = new GridBagConstraints();
		this.table = new JTable(this.tableModel);		
		this.table.setOpaque(true);
		this.table.setFillsViewportHeight(true);
		this.table.setRowHeight(DEF_ROW_HEIGHT);
		TableRowSorter<AttributeTableModel> sorter = new TableRowSorter<AttributeTableModel>(this.tableModel);
		sorter.setSortable(0, true);
		sorter.setSortable(1, true);
		sorter.setSortable(2, false);
		sorter.setSortable(3, true);
		this.table.setRowSorter(sorter);
		this.table.setShowGrid(true);
		this.table.setDefaultRenderer(String.class, new TableCellTooltipRender());
		this.table.getTableHeader().setReorderingAllowed(false);
		//---------------------------------------
		AttributeTableCellRenderer renderer = new AttributeTableCellRenderer();
		TableColumn col0 = this.table.getColumnModel().getColumn(0);
		TableColumn col2 = this.table.getColumnModel().getColumn(2);
		col0.setCellRenderer(renderer);
		col2.setCellRenderer(renderer);
		//---------------------------------------
		AttributeTableCellEditor editor = new AttributeTableCellEditor(this.tableModel, renderer);
		col0.setCellEditor(editor);
		col2.setCellEditor(editor);
		// Package of interface
		if(this.graphView != null) {
			this.scroll = new JScrollPane(this.table);
			gbc = new GridBagConstraints(0,0,  3,1,  1,1,  GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5),  0,0);
			this.view.add(scroll,gbc);
			
			gbc = new GridBagConstraints(0,1,  1,1,  0,0,  GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 5, 5, 5),  0,0);
			this.view.add(this.selectAllLabel,gbc);
	
			gbc = new GridBagConstraints(1,1,  1,1,  0,0,  GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 5, 5, 5),  0,0);
			this.view.add(this.selectAll,gbc);
			this.selectAll.setSelected(checkAll);
			
			gbc = new GridBagConstraints(2,1,  1,1,  1,0,  GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 50, 5, 50),  0,0);
			this.view.add(this.applyButton,gbc);
		}
		this.view.updateUI();
	}
	
	private void init() {
		this.view = new JPanel(new GridBagLayout());
		this.applyButton = new JButton("Apply");
		this.selectAll = new JCheckBox();
		this.selectAllLabel = new JLabel("Select all :");
		this.applyButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Thread t = new Thread(new Runnable() {
					@Override
					public void run() {
						synchronized (generalMutex) {
							apply();
						}
					}
				});
				t.setDaemon(true);
				t.start();
			}
		});
		this.selectAll.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				synchronized (generalMutex) {
					boolean check = AttributePanel.this.selectAll.isSelected();
					for(int i = 0 ; i < AttributePanel.this.tableModel.getRowCount() ; i++) {
						AttributeShower shower = (AttributeShower)AttributePanel.this.tableModel.getValueAt(i, 0);
						if(check) {
							shower.show();
						} else {
							shower.unshow();
						}
					}
					if(SwingUtilities.isEventDispatchThread()) {
						table.editingStopped(null);
						view.updateUI();
					} else {
						SwingUtilities.invokeLater(new Runnable() {
							public void run() {
								synchronized (generalMutex) {
									table.editingStopped(null);
									view.updateUI();
								}
							}
						});
					}
				}
			}
		});
		this.view.setVisible(true);
	}
}
